package main

import (
	"database/sql"
	"flag"
	"fmt"
	"os"
	"path"

	"github.com/gocarina/gocsv"

	_ "github.com/go-sql-driver/mysql"
)

var (
	user   = flag.String("user", "root", "user")
	pass   = flag.String("password", "nikki", "password")
	host   = flag.String("host", "127.0.0.1", "host")
	port   = flag.String("port", "3306", "port")
	output = flag.String("output", "output", "output directory")
)

func init() {
	flag.Parse()
}

type TableInfo struct {
	Field       string
	Type        string
	Collation   sql.NullString `csv:"-"`
	Null        string
	Key         string
	DefaultNull sql.NullString `csv:"-"`
	Default     string
	Extra       string
	Privileges  string `csv:"-"`
	Comment     string
}

func main() {
	os.MkdirAll(*output, os.ModePerm)

	mysql, err := sql.Open("mysql",
		fmt.Sprintf("%s:%s@tcp(%s:%s)/?loc=Local&charset=utf8",
			*user, *pass, *host, *port))
	if err != nil {
		panic(err)
	}
	defer mysql.Close()

	databases := []string{}
	func() {
		rows, err := mysql.Query("SHOW DATABASES")
		if err != nil {
			panic(err)
		}
		defer rows.Close()

		for rows.Next() {
			v := ""
			err := rows.Scan(&v)
			if err != nil {
				panic(err)
			}
			if !ignore(v) {
				databases = append(databases, v)
			}
		}
	}()

	for _, database := range databases {
		_, err := mysql.Exec(fmt.Sprintf("USE %v", database))
		if err != nil {
			panic(err)
		}

		tables := []string{}
		func() {
			rows, err := mysql.Query("SHOW TABLES")
			if err != nil {
				panic(err)
			}
			defer rows.Close()

			for rows.Next() {
				v := ""
				err := rows.Scan(&v)
				if err != nil {
					panic(err)
				}
				tables = append(tables, v)
			}
		}()

		for _, table := range tables {
			tableInfos := []*TableInfo{}
			func() {
				rows, err := mysql.Query(fmt.Sprintf("SHOW FULL COLUMNS FROM %s", table))
				if err != nil {
					panic(err)
				}
				defer rows.Close()

				for rows.Next() {
					tableInfo := &TableInfo{}
					if err := rows.Scan(
						&tableInfo.Field,
						&tableInfo.Type,
						&tableInfo.Collation,
						&tableInfo.Null,
						&tableInfo.Key,
						&tableInfo.DefaultNull,
						&tableInfo.Extra,
						&tableInfo.Privileges,
						&tableInfo.Comment,
					); err != nil {
						panic(err)
					}

					if tableInfo.DefaultNull.Valid {
						tableInfo.Default = tableInfo.DefaultNull.String
					} else {
						tableInfo.Default = "NULL"
					}

					tableInfos = append(tableInfos, tableInfo)
				}
			}()

			file, err := os.Create(path.Join(*output, fmt.Sprintf("%v-%v.csv", database, table)))
			if err != nil {
				panic(err)
			}
			defer file.Close()

			err = gocsv.MarshalFile(&tableInfos, file)
			if err != nil {
				panic(err)
			}
		}
	}
}

func ignore(database string) bool {
	list := []string{"information_schema", "mysql", "performance_schema", "sys", "test"}
	for i := 0; i < len(list); i++ {
		if list[i] == database {
			return true
		}
	}
	return false
}
